package com.drool.mediaplayer;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.drool.mediaplayer.Database.MediaFilesDBHelper;

import android.app.Activity;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.MediaStore.Audio;
import android.util.Log;

import com.drool.mediaplayer.Database.MediaFilesDB;
import com.drool.mediaplayer.Database.MediaFilesDB.*;

public class Playlist {
	
	private static final String TAG = "Playlist";
	
	private long activePlaylist;
	private Cursor playlistCursor;
	private Context context;
	private long playlistType;
	private MediaFilesDBHelper myDBHelper;
	private SQLiteDatabase db;
	private int shuffle = 0; //0=shuffle off 1 = shuffle on
	private int loop = 0;//0=loopoff 1= loopon ; song loop only
	private int playlistCursorOpened;
	private int currentSongIndex;
	//private int currentSongIndex;
	private List<Integer> alreadyPlayed;//this list will hold the song indices that have been played already
	private Random rndGen;
	
	public static final long PLAYLIST_TYPE_NEW = -1;
	public static final long PLAYLIST_TYPE_OLD = 0;
	public static final int PLAYLIST_SHUFFLE_ON = 1;
	public static final int PLAYLIST_SHUFFLE_OFF = 0;
	public static final int PLAYLIST_LOOP_ON = 1;
	public static final int PLAYLIST_LOOP_OFF = 0;
	public static final int PLAYLIST_CURSOR_OPEN = 1;
	public static final int PLAYLIST_CURSOR_CLOSE = 0;
	
	Playlist(long PlaylistId, Context context, String playlistName){
		//while creating a playlist object the playlist ID can be passed by the acivity. The id can be stored
		//by the activity in a preference while the activity is live
		
		
		if (PlaylistId == PLAYLIST_TYPE_NEW){
			//create a new playlist and set the active playlist to be the 
			// id of the new playlist
			MediaFilesDBHelper myDBHelper = new MediaFilesDBHelper(context);
			SQLiteDatabase tempDB = myDBHelper.getReadableDatabase();
			
			Cursor tempCursor = tempDB.query(MediaFilesDB.Playlist.PLAYLIST_TABLE_NAME 
					,new String[] {MediaFilesDB.Playlist._ID, MediaFilesDB.Playlist.PLAYLIST_NAME} ,
					MediaFilesDB.Playlist.PLAYLIST_NAME + "=\'" + 
					playlistName + "\'", 
					null, null, null, null);
			
			if (tempCursor.getCount() > 0){
				tempCursor.moveToFirst();
				Log.i(TAG, "Playlist " + tempCursor.getString(1)+ " selected");
				activePlaylist = Long.valueOf(tempCursor.getString(0));
				tempCursor.close();
			}else{
				tempCursor.close();
				SQLiteDatabase dbWritable = myDBHelper.getWritableDatabase();	
				
				dbWritable.beginTransaction();
				try{
					ContentValues values = new ContentValues();
					//values.put(MediaFilesDB.FileHeader.HDR_FILE_NAME, v.mCursorVideo.getString(1));
					values.put(MediaFilesDB.Playlist.PLAYLIST_NAME, playlistName);
					
					Calendar calendar = Calendar.getInstance();
					SimpleDateFormat dateFormat = new SimpleDateFormat ("ddMMyyyyHHmmssSS");				
					
					values.put(MediaFilesDB.Playlist.PLAYLIST_CREATED_ON,dateFormat.format(calendar.getTime()) );
					
					activePlaylist = dbWritable.insert(MediaFilesDB.Playlist.PLAYLIST_TABLE_NAME, null, values);
					dbWritable.setTransactionSuccessful();
				}catch(Exception e){
					Log.e(TAG, "DB Update failed");
					//TODO code rollback - three inserts have to be rollbacked potentially
					activePlaylist = -1;
					dbWritable.close();				
				}finally{
					dbWritable.endTransaction();
				}
				dbWritable.close();
			}
			tempDB.close();
			myDBHelper.close();
				
			playlistType = PLAYLIST_TYPE_NEW;
		}
		else{
			activePlaylist = PlaylistId;
			playlistType = PLAYLIST_TYPE_OLD;
		}
		
		
		playlistCursor  = null;
		this.context = context;
		playlistCursorOpened = PLAYLIST_CURSOR_CLOSE;
		rndGen = new Random(System.currentTimeMillis());
		alreadyPlayed = new ArrayList<Integer> ();
		//clearAlreadyPlayedList();
	}
	
	Playlist(long PlaylistId, Context context){
		//while creating a playlist object the playlist ID can be passed by the acivity. The id can be stored
		//by the activity in a preference while the activity is live
		
		if (PlaylistId == PLAYLIST_TYPE_NEW){
			activePlaylist = -1;
		}
		else
			activePlaylist = PlaylistId;
		
		playlistType = PLAYLIST_TYPE_OLD;
		playlistCursor  = null;
		this.context = context;	
		playlistCursorOpened = PLAYLIST_CURSOR_CLOSE;
		rndGen = new Random(System.currentTimeMillis());
		alreadyPlayed = new ArrayList<Integer> ();
	}
	
	public long getActivePlaylist(){
		//use this method after a call to constructor, to find out if a valid playlist
		//has been attached with this object. this function will returna -1 in case an invalid playlist 
		//has been attached
		
		return activePlaylist;
	}
	
	public long updatePlaylist(String songId) throws InvalidPlaylistException{
		long row = -1;
		
		MediaFilesDBHelper myDBHelper = new MediaFilesDBHelper(context);
		
		SQLiteDatabase dbWritable = myDBHelper.getWritableDatabase();	
		
		InvalidPlaylistException e = new InvalidPlaylistException();
		
		if (activePlaylist == -1)
			throw e;
		else{
			dbWritable.beginTransaction();
			try{
				ContentValues values = new ContentValues();
				values.clear();
				
				//values.put(MediaFilesDB.VideoFiles.VIDEO_ALBUM, v.mCursorVideo.getString(3));
				values.put(PlaylistItems.PLAYLIST_ITEMS_PL_ID, activePlaylist);
				values.put(PlaylistItems.PLAYLIST_ITEMS_MEDIA_ID, songId);
				
				Uri audio = Audio.Media.EXTERNAL_CONTENT_URI;
				
				Cursor c = ((Activity) context).managedQuery(audio, new String []{
						Audio.Media._ID, Audio.Media.TITLE
				}, Audio.Media._ID + "=" + songId, null, null);
				
				if (c.getCount() > 0){
					c.moveToFirst();
					values.put(PlaylistItems.PLAYLIST_ITEMS_MEDIA_NAME, c.getString(1));
				}
				c.close();
				
				values.put(PlaylistItems.PLAYLIST_ITEMS_MEDIA_TYPE,"a");			
				
				row = dbWritable.insert(PlaylistItems.PLAYLIST_ITEMS_TABLE_NAME, null, values);
				values.clear();
				dbWritable.setTransactionSuccessful();
				Log.i(TAG, "DB Update OK");
				
			}catch(Exception e1){
				Log.e(TAG, "DB Update failed");
				//TODO code rollback - three inserts have to be rollbacked potentially
				dbWritable.close();
				return row;
			}finally{
				dbWritable.endTransaction();
			}
			
			//test only
			//	openPlaylistCursor();
			//	Log.i(TAG, "After playlist update it has " + Integer.toString(playlistCursor.getCount()) + " items");
			//test end
			
			dbWritable.close();
			myDBHelper.close();
			
			return row;
		}
	}
	
	public Cursor getPlaylist() throws PlaylistException{
		//setPlaylistCursor();
		if (playlistCursorOpened == PLAYLIST_CURSOR_CLOSE){
			throw (new PlaylistException());
		}
		return playlistCursor;
		
	}
	
	public void openPlaylistCursor() throws InvalidPlaylistException{
		if (activePlaylist == -1){
			throw (new InvalidPlaylistException());
		}else{
			
			myDBHelper = new MediaFilesDBHelper(context);	
			db = myDBHelper.getReadableDatabase();
			playlistCursor = db.query(MediaFilesDB.Playlist.PLAYLIST_TABLE_NAME + ","
					+ MediaFilesDB.PlaylistItems.PLAYLIST_ITEMS_TABLE_NAME
					,new String[] {MediaFilesDB.PlaylistItems.PLAYLIST_ITEMS_TABLE_NAME +"." 
					+ PlaylistItems._ID
					, PlaylistItems.PLAYLIST_ITEMS_MEDIA_ID, PlaylistItems.PLAYLIST_ITEMS_MEDIA_NAME} ,
					PlaylistItems.PLAYLIST_ITEMS_PL_ID  + " =" +
					MediaFilesDB.Playlist.PLAYLIST_TABLE_NAME + "." + MediaFilesDB.Playlist._ID
					+ " and " + PlaylistItems.PLAYLIST_ITEMS_MEDIA_TYPE +  "=\'a\'"
					+ " and " + MediaFilesDB.Playlist.PLAYLIST_TABLE_NAME + "." + MediaFilesDB.Playlist._ID 
					+ "=" + activePlaylist , 
					null, null, null, null);
			((Activity) context).startManagingCursor(playlistCursor);
			Log.i(TAG, "Playlist has " + Integer.toString(playlistCursor.getCount()));
			
			playlistCursorOpened = PLAYLIST_CURSOR_OPEN;
		}	
	}
	
	public void closePlaylist(){
		playlistCursor.close();
		db.close();
		myDBHelper.close();
		playlistCursorOpened = PLAYLIST_CURSOR_CLOSE;
	}
	
	public boolean isPlaylistCursorOpened(){
		if (playlistCursorOpened == PLAYLIST_CURSOR_OPEN){
			return true;
		}else
			return false;
	}
	
	public void setShuffle(int shuffleState){

		switch (shuffleState){
			case PLAYLIST_SHUFFLE_OFF:
				shuffle = PLAYLIST_SHUFFLE_OFF;
				break;
			case PLAYLIST_SHUFFLE_ON:
				shuffle = PLAYLIST_SHUFFLE_ON;
				break;	
			default: 
				shuffle = PLAYLIST_SHUFFLE_OFF;
				break;
		}
	}
	
	public int getShuffleState(){
		return shuffle;
	}
	
	public void setLoop(int loopState){
		switch (loopState){
			case PLAYLIST_LOOP_OFF:
				loop = PLAYLIST_LOOP_OFF;
				break;
			case PLAYLIST_LOOP_ON:
				loop = PLAYLIST_LOOP_ON;
				break;	
			default: 
				loop = PLAYLIST_LOOP_OFF;
				break;
		}
	}
	
	public int getLoopState(){
		return loop;
	}
	
	public void setCurrentSongId(String songId, long row) throws PlaylistException{
		if (playlistCursorOpened == PLAYLIST_CURSOR_CLOSE)
			throw (new PlaylistException());
		else{
			Log.i(TAG, "playlistcursr has " + Integer.toString(playlistCursor.getCount()));
			
			playlistCursor.moveToFirst();
			while (!playlistCursor.isAfterLast()){
				Log.i(TAG, "mediaID=" + Integer.toString(playlistCursor.getInt(1)) + " " + " songid=" + songId + 
						"  _id=" + playlistCursor.getString(0)
						+ " row = " + Long.toString(row));
				if (playlistCursor.getInt(1) == Integer.valueOf(songId)
						&& playlistCursor.getString(0).equals(Long.toString(row))){
					currentSongIndex = playlistCursor.getPosition();
					Log.i(TAG, "Current song id = " + Integer.toString(currentSongIndex));
					break;
				}
				else
					Log.i(TAG, "no match");
				playlistCursor.moveToNext();
			}
				
		}
		Log.i(TAG, "Current song id = " + Integer.toString(currentSongIndex));
	}
	
	public void setCurrentSongId(int songIndex){
		currentSongIndex = songIndex;
		Log.i(TAG, "Current song id = " + Integer.toString(currentSongIndex));
	}
	
	public int getCurrentSongId(){
		Log.i(TAG, "returning Current song id = " + Integer.toString(currentSongIndex));
		return currentSongIndex;
	}
	
	public void cursorRequery(){
		playlistCursor.requery();
	}
	
	public String getNextSong() throws PlaylistException{
		Log.i(TAG, "Current song id = " + Integer.toString(currentSongIndex));
		Log.i(TAG, "shuffle = " + Integer.toString(shuffle) + " loop = " + Integer.toString(loop));
		if (playlistCursorOpened == PLAYLIST_CURSOR_CLOSE)
			throw (new PlaylistException());
		else{
			if (currentSongIndex == -1){
				if (playlistCursor.getCount()> 0){
					playlistCursor.moveToFirst();
					currentSongIndex = playlistCursor.getPosition();
					Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
					return playlistCursor.getString(1);
				}else{
					Log.i(TAG, "returning null because playlist empty");
					return null;
				}
			}else{
			
				playlistCursor.moveToPosition(currentSongIndex);
				if (shuffle == PLAYLIST_SHUFFLE_OFF){
					playlistCursor.moveToNext();
					if (playlistCursor.isAfterLast()){
						if (loop == PLAYLIST_LOOP_ON){
							clearAlreadyPlayedList();
							playlistCursor.moveToFirst();
						}
						else{
							Log.i(TAG, "returning null because end of playlist reached");	
							return null;
						}
					}	
					currentSongIndex = playlistCursor.getPosition();
					Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
					return playlistCursor.getString(1);
				}
				else{
					int nextIndex = getNextRandomIndex();
					if (nextIndex!= -1){
						playlistCursor.moveToPosition(nextIndex);
						currentSongIndex = playlistCursor.getPosition();
						Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
						return playlistCursor.getString(1);
					}
					else{
						if (loop == PLAYLIST_LOOP_ON){
							clearAlreadyPlayedList();
							nextIndex = getNextRandomIndex();
							playlistCursor.moveToPosition(nextIndex);
							currentSongIndex = playlistCursor.getPosition();
							Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
							return playlistCursor.getString(1);
						}else{
							Log.i(TAG, "returning null because end of playlist reached in random mode");	
							return null;
						}
					}
				}
			}	
		}
	}
	
	private void clearAlreadyPlayedList(){
		Iterator it = alreadyPlayed.listIterator();
		while (it.hasNext()){
			it.next();
			it.remove();
		}
	}
	
	private int getNextRandomIndex(){
		
		int playlistCount = playlistCursor.getCount();
		
		//get a random number from 0..playlistCount
		//check to see if it is in the alreadyPlayedlist
		//if yes, get another random number
		//if no return the number
		//if no number can be returned, return -1;
		boolean done = false;
		int rndIndex;
		int skipped = 0;
		
		while(!done && playlistCount != 0){
			rndIndex = rndGen.nextInt(playlistCount);
			
			if (alreadyPlayed.contains(rndIndex)){
				skipped++;
				if (skipped == playlistCount){
					break;
				}else
					continue;
			}
			else
				return rndIndex;
		}
		return -1;
	}
	
	
	public String getPrevSong() throws PlaylistException{
		Log.i(TAG, "shuffle = " + Integer.toString(shuffle) + " loop = " + Integer.toString(loop));
		
		if (playlistCursorOpened == PLAYLIST_CURSOR_CLOSE)
			throw (new PlaylistException());
		else{
			if (currentSongIndex == -1){
				if (playlistCursor.getCount()> 0){
					playlistCursor.moveToFirst();
					currentSongIndex = playlistCursor.getPosition();
					Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
					return playlistCursor.getString(1);
				}else{
					return null;
				}
			}else{
				playlistCursor.moveToPosition(currentSongIndex);
				Log.i(TAG, "Current song id = " + Integer.toString(currentSongIndex));
				if (shuffle == PLAYLIST_SHUFFLE_OFF){
					playlistCursor.moveToPrevious();
					if (playlistCursor.isBeforeFirst()){
						if (loop == PLAYLIST_LOOP_ON){
							clearAlreadyPlayedList();
							playlistCursor.moveToLast();
						}else
							return null;
					}	
					currentSongIndex = playlistCursor.getPosition();
					Log.i(TAG, "prev song id = " + Integer.toString(currentSongIndex));
					return playlistCursor.getString(1);
				}else{
					int nextIndex = getNextRandomIndex();
					if (nextIndex!= -1){
						playlistCursor.moveToPosition(nextIndex);
						currentSongIndex = playlistCursor.getPosition();
						Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
						return playlistCursor.getString(1);
					}
					else{
						if (loop == PLAYLIST_LOOP_ON){
							clearAlreadyPlayedList();
							nextIndex = getNextRandomIndex();
							playlistCursor.moveToPosition(nextIndex);
							currentSongIndex = playlistCursor.getPosition();
							Log.i(TAG, "next song id = " + Integer.toString(currentSongIndex));
							return playlistCursor.getString(1);
						}else
							return null;
					}
				}
			}	
			
		}
	}
	
	public void addAlreadyPlayedItem(int songIndex){
		alreadyPlayed.add(songIndex);		
	}
	
	public List<Integer> getAlreadyPlayedItemList(){
		return alreadyPlayed;
	}
	
	public void removeFromAlreadyPlayed(int songIndex){
		if (alreadyPlayed.contains((int) songIndex)){
			int ind = alreadyPlayed.indexOf((int)songIndex);
			Log.i(TAG, "removing from already played " + Integer.toString(songIndex));
			alreadyPlayed.remove(ind);
		}
	}
	
	public void adjustAlreadyPlayed(int removedSongIndex){
		
		List<Integer> tempList = new ArrayList<Integer> ();
		
		Iterator<Integer> it = alreadyPlayed.iterator();
		int nxt;
		
		while (it.hasNext()){
			if ((nxt = it.next()) > removedSongIndex){				
				tempList.add(nxt);							
			}
		}
		
		if (!tempList.isEmpty()){
			it = tempList.iterator();
			while (it.hasNext()){
				int ind = alreadyPlayed.indexOf(nxt = it.next());
				Log.i(TAG, "adjusting " + Integer.toString(nxt));
				alreadyPlayed.remove(ind);
				alreadyPlayed.add(nxt -1);
			}
		}
		
		
	}
	
	
}


class PlaylistException extends Exception{	
	PlaylistException(){
		super();
	}
	
	@Override
	public String getMessage() {
		// TODO Auto-generated method stub
		return "Playlist Error";
	}
}

class InvalidPlaylistException extends PlaylistException{

	InvalidPlaylistException(){
		super();
	}
	
	@Override
	public String getMessage() {
		// TODO Auto-generated method stub
		return "Invalid playlist attached";
	}
	
}

